// Copyright (c) 2011 SameGoal LLC.
// All Rights Reserved.
// Author: Andy Hochhaus <ahochhaus@samegoal.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// WARNING: Do not use this code in production. It has known security flaws.
//          ONLY USE THIS SERVER FOR LEARNING PURPOSES!

#include <map>
#include <string>

#include "gflags/gflags.h"
#include "glog/logging.h"

#include "base/init.h"
#include "net/http/server/comet/comet.h"
#include "net/http/server/comet/demo/chat.htm.h"
#include "net/http/server/comet/demo/g.js.h"
#include "net/http/server/comet/demo/g.css.h"
#include "net/http/server/comet/json.h"
#include "net/http/server/event/handler.h"

DEFINE_string(http_hostname, "localhost", "hostname for HTTPS server.");

namespace sg {
namespace net {
namespace http {
namespace server {
namespace comet {
namespace demo {

const char *usage = "\n"
"Comet Chat HTTP Server.\n";

CometServer *server = NULL;

void CssHandler(es::Request *req, void */* arg */) {
  req->AddResponseHeader("Content-Type", "text/css");
  RequestHelper req_helper(req);
  CHECK(req_helper.Send(es::OK,
                        net_http_server_comet_demo_g_css_str,
                        RequestHelper::NONE));
}

void JsHandler(es::Request *req, void */* arg */) {
  req->AddResponseHeader("Content-Type", "text/javascript");
  RequestHelper req_helper(req);
  CHECK(req_helper.Send(es::OK,
                        net_http_server_comet_demo_g_js_str,
                        RequestHelper::NONE));
}

void HtmlHandler(es::Request *req, void */* arg */) {
  req->AddResponseHeader("Content-Type", "text/html; charset=utf-8");
  RequestHelper req_helper(req);
  CHECK(req_helper.Send(es::OK,
                        net_http_server_comet_demo_chat_htm_str,
                        RequestHelper::NONE));
}

typedef std::map<std::string, std::string> StringMap;
StringMap session_users;

void CreateSessionHandler(const std::string &sid,
                          es::Request */* req */) {
  session_users[sid] = "Anonymous";
}

void CloseSessionHandler(const std::string &sid) {
  session_users.erase(sid);
  // delete from all other users
  for (StringMap::iterator i = session_users.begin();
       i != session_users.end(); ++i) {
    JsonArray msg;
    msg.AppendString("delete");
    msg.AppendString(sid);
    server->Send(i->first, msg.Serialize());
  }
}

bool ChannelHandler(const std::string &sid,
                    const CometServer::IncomingMap &map,
                    es::Request */* req */) {
  if (map.find("user") != map.end()) {
    std::string user = map.find("user")->second;
    session_users[sid] = user;

    // send new user to all other users
    for (StringMap::iterator i = session_users.begin();
         i != session_users.end(); ++i) {
      if (i->first == sid) {
        continue;
      }
      JsonArray msg;
      const std::string user = session_users.find(sid) != session_users.end() ?
          session_users[sid] : "Stale";
      msg.AppendString("new");
      msg.AppendString(user);
      msg.AppendString(sid);
      server->Send(i->first, msg.Serialize());
    }

    // send all users to new user
    for (StringMap::iterator i = session_users.begin();
         i != session_users.end(); ++i) {
      JsonArray msg;
      msg.AppendString("new");
      msg.AppendString(i->second);
      msg.AppendString(i->first);
      server->Send(sid, msg.Serialize());
    }
  } else if (map.find("message") != map.end()) {
    std::string message = map.find("message")->second;
    for (StringMap::iterator i = session_users.begin();
         i != session_users.end(); ++i) {
      JsonArray msg;
      const std::string user = session_users.find(sid) != session_users.end() ?
          session_users[sid] : "Stale";
      msg.AppendString("message");
      msg.AppendString(user);
      msg.AppendString(message);
      server->Send(i->first, msg.Serialize());
    }
  }
  return true;
}

}  // namespace demo
}  // namespace comet
}  // namespace server
}  // namespace http
}  // namespace net
}  // namespace sg

namespace demo = ::sg::net::http::server::comet::demo;
namespace es = ::sg::net::http::server::event;

int main(int argc, char *argv[]) {
  SG_INIT(demo::usage, &argc, &argv, true);

  demo::server =
      new sg::net::http::server::comet::CometServer(
          "0.0.0.0", FLAGS_http_hostname, "8080", 128);
  demo::server->SetHostPrefix("");
  demo::server->SetBlockedPrefix("");
  demo::server->SetHeadersHelper(new ::sg::net::http::headers::Helper());
  demo::server->InstallCometHandlers();
  demo::server->SetCreateSessionHandler(&demo::CreateSessionHandler);
  demo::server->SetCloseSessionHandler(&demo::CloseSessionHandler);
  demo::server->SetChannelHandler(&demo::ChannelHandler);
  es::Handler css(&demo::CssHandler, NULL);
  css.set_uri_regex("/css", NULL);
  demo::server->AddHandler(&css);
  es::Handler js(&demo::JsHandler, NULL);
  js.set_uri_regex("/js", NULL);
  demo::server->AddHandler(&js);
  es::Handler html(&demo::HtmlHandler, NULL);
  html.set_uri_regex("/", NULL);
  demo::server->AddHandler(&html);
  demo::server->Serve();

  return 0;
}
